from typing import List, Dict, Any
import time
from pydantic import BaseModel
import pandas as pd
from calc_manager.plugins_inner.op2_inner import OP2Plugin
from calc_manager.plugins_inner.bdf_inner import BDFPlugin
from calc_manager.plugins_inner.storage_plugin import StoragePlugin
import re
from re import search, I
from scipy.interpolate import interp1d
import numpy as np

class Parameter(BaseModel):
    bdf_model: str
    op2_fp: str


class Result(BaseModel):
    version: str
    timestamp: str
    groups: List[Any]


class ShellStressItem:
    fiber_distance = 0
    oxx = 1
    oyy = 2
    txy = 3
    angle = 4
    omax = 5
    omin = 6
    von_mises = 7


cfq_dict = {}


def get_mat_from_csv(mid):
    if mid not in cfq_dict:
        # print(f"{mid} 不存在材料数据库中,默认不存在疲劳风险,忽略")
        return
    else:
        prop = {}
        if re.match(r"^3[0-9]{5}$", str(mid)):
            # print("这是钛")
            prop["name"] = "ti"
            prop["C"] = 0.9
            prop["fm0"] = 620
            prop["q"] = 6.5
        if re.match(r"^4[0-9]{5}$", str(mid)):
            # print("这是钢")
            prop["name"] = "st"
            prop["C"] = 0.91
            prop["fm0"] = 930
            prop["q"] = 7.5
        else:
            # print("这是铝")
            prop["name"] = "al"
            prop["C"] = 0.86
            prop["fm0"] = 310
            prop["q"] = 4.0
        prop["cutoff"] = cfq_dict[mid]
        return prop


# 根据单元编号区分所属机翼还是机身
class RegexMap:
    def __init__(self, n_dic, val):
        self._items = n_dic
        self.__val = val

    def __getitem__(self, key):
        key = str(key)
        for regex in self._items.keys():
            if search(regex, key, I):
                return self._items[regex]
        return self.__val


def get_section_name_from_eid(eid: int):
    # 精细模型单元编号，共8位
    section_dict = {
        r"^1[0-4][0-9]{6}$": "S11",
        r"^1[5-9][0-9]{6}$": "S12",
        r"^2[0-9][0-9]{6}$": "S20",
        r"^3[0-6][0-9]{6}$": "S31",
        r"^3[7-9][0-9]{6}$": "S32",
        r"^4[0-4][0-9]{6}$": "S40",
        r"^5[0-4][0-9]{6}$": "S50",
        r"^6[0-4][0-9]{6}$": "S60",
        r"^7[0-9][0-9]{6}$": "S70",
        r"^8[0-9][0-9]{6}$": "S80",
        r"^9[0-4][0-9]{6}$": "S90",
    }
    reg_section_dict = RegexMap(section_dict, "S00")
    return reg_section_dict[eid]


def get_fatigue_damage_ratio(eid: int, center: list[float]):
    section_num = int(get_section_name_from_eid(eid)[1:])
    if section_num > 40:
        # z向插值
        f = interp1d(df_oef_z.iloc[:, 0], df_oef_z.iloc[:, 1])
        return float(f(center[2])[0])
    else:
        # x向插值
        f = interp1d(df_oef_x.iloc[:, 0], df_oef_x.iloc[:, 1])
        return float(f(center[0]))


def translate_stress_matrix(stress: list[float], angle: np.float64):
    stress_matrix = np.array([[stress[0], stress[2]], [stress[2], stress[1]]])
    transform_angle_matrix = np.array(
        [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]
    )
    left_result = np.matmul(np.transpose(transform_angle_matrix), stress_matrix)
    right_result = np.matmul(left_result, transform_angle_matrix)
    transformed_stress_x = right_result[0][0]
    transformed_stress_y = right_result[1][1]
    transformed_single_axis_stress = transformed_stress_x - 1 / 3 * transformed_stress_y
    return transformed_single_axis_stress


def modify_ratio(ratio: float):
    return ((1 - ratio) / 0.94) ** 0.58


def get_max_equal_stress(ratio: float, stress_max: float, mat_prop: dict):
    if ratio <= 0.6:
        return modify_ratio(ratio) * stress_max
    else:
        num1 = 2.5 * (1 - ratio) * mat_prop["fm0"] * stress_max
        num2 = (
            mat_prop["fm0"]
            + 2 * (1 - ratio) * stress_max
            - 0.5 * (1 + ratio) * stress_max
        )
        return modify_ratio(0.6) * num1 / num2


# 计算疲劳循环次数
def get_fatigue_cycle(stress_range: list[float], mid: int, S: float):
    # CFQ和q值,如果未找到,忽略
    prop = get_mat_from_csv(mid)
    if prop:
        cfq = prop["cutoff"]
        q = prop["q"]
        # 根据应力幅计算应力比 应力比对疲劳计算是有影响的
        stress_ratio = min(stress_range) / max(stress_range)
        if stress_ratio >= -2:
            cw = 1
        elif stress_ratio < -10:
            cw = 0.5
        else:
            cw = 0.00893 * stress_ratio**2 + 0.16966 * stress_ratio + 1.3036
        # 应力比修正
        stress_ratio = stress_ratio * cw
        # 修正系数,取保守值1
        f_sd = 1
        max_equal_stress = get_max_equal_stress(stress_ratio, max(stress_range), prop)
        # 根据公式计算疲劳循环次数
        cycles = S * 100000 * (cfq / max_equal_stress) ** q * f_sd / 10**10
        return cycles
    else:
        # 返回一个极大值
        # print(f"{mid} 没有被找到!!!!!!!!!!")
        return 99999999

summary_info = []


def evaluate_fatigue(element, stress_result, center, g70):
    if stress_result is None:
        return
    result = {"bottom": {}, "top": {}}
    mid = element.property.mids[0]
    if get_mat_from_csv(mid) is None:
        return
    stress_bottom = []
    stress_top = []
    for stress in stress_result.data:
        if stress.location == 'z1' and stress.element_node == 0:
            stress_top.append(
                [stress.data[index] for index in [ShellStressItem.oxx, ShellStressItem.oyy, ShellStressItem.txy]])
        elif stress.location == 'z2' and stress.element_node == 0:
            stress_bottom.append(
                [stress.data[index] for index in [ShellStressItem.oxx, ShellStressItem.oyy, ShellStressItem.txy]])

    S = get_fatigue_damage_ratio(element.eid, center)
    # 处理所有角度
    axis_stress_bottom = []
    axis_stress_top = []
    for i in range(0, 18):
        angle = np.pi / 18 * i
        temp_stress1, temp_stress2 = [], []
        for stress in stress_bottom:
            temp_stress1.append(translate_stress_matrix(stress, angle))
        for stress in stress_top:
            temp_stress2.append(translate_stress_matrix(stress, angle))
        # 第一个元素为最小单轴应力  第二个元素为最大单轴应力  本质上是计算应力幅值
        axis_stress_bottom.append(temp_stress1)
        axis_stress_top.append(temp_stress2)
        result["bottom"][i] = get_fatigue_cycle(
            [min(temp_stress1), max(temp_stress1)], mid, S
        )
        result["top"][i] = get_fatigue_cycle(
            [min(temp_stress2), max(temp_stress2)], mid, S
        )

    # 判断该单元的疲劳循环次数是否满足要求
    all_cycles = list(result["bottom"].values()) + list(result["top"].values())
    danger_angle_index_bottom, danger_angle_index_top = [], []
    for index, cycle in enumerate(list(result["bottom"].values())):
        if cycle <= 10 ** 5:
            danger_angle_index_bottom.append(index)
    for index, cycle in enumerate(list(result["top"].values())):
        if cycle <= 10 ** 5:
            danger_angle_index_top.append(index)
    if len(danger_angle_index_top) + len(danger_angle_index_bottom) == 0:
        pass
    else:
        CFQ = get_mat_from_csv(mid)["cutoff"]
        fm0 = get_mat_from_csv(mid)["fm0"]
        s = get_mat_from_csv(mid)["q"]
        for index in danger_angle_index_bottom:
            if index < 10:
                elemid = f"{element.eid}@10{index}"
            else:
                elemid = f"{element.eid}@1{index}"
            base = [g70, elemid, "", "1.0;1.5", CFQ, fm0, s]
            base.extend(axis_stress_bottom[index])
            summary_info.append(base)
        for index in danger_angle_index_top:
            if index < 10:
                elemid = f"{element.eid}@20{index}"
            else:
                elemid = f"{element.eid}@2{index}"
            base = [g70, elemid, "", "1.0;1.5", CFQ, fm0, s]
            base.extend(axis_stress_top[index])
            summary_info.append(base)


def fatigue(param: Parameter, bdf_plugin: BDFPlugin, op2_plugin: OP2Plugin, storage_plugin: StoragePlugin) -> Result:
    # 读取材料文件
    storage_plugin.get_file("dc_storage_plugin/mat_csv/CFQcutoff.csv", 'CFQcutoff.csv')
    cfq_df = pd.read_csv('CFQcutoff.csv')
    for index, row in cfq_df.iterrows():
        cfq_dict[row.iloc[4]] = row.iloc[-1]

    storage_plugin.get_file("dc_storage_plugin/mat_csv/MSCoef_x.csv", 'MSCoef_x.csv')
    global df_oef_x, df_oef_z
    df_oef_x = pd.read_csv('MSCoef_x.csv')
    storage_plugin.get_file("dc_storage_plugin/mat_csv/MSCoef_z.csv", 'MSCoef_z.csv')
    df_oef_z = pd.read_csv('MSCoef_z.csv')

    # 获取所有精细网格
    refine_elements = bdf_plugin.get_elements(param.bdf_model, element_types=['ctria3', 'cquad4'], refine=True)
    print(f'refine elements num: {len(refine_elements)}')
    refine_eids = [refine_element.eid for refine_element in refine_elements]

    op2_spec = op2_plugin.get_op2_spec(param.op2_fp)
    stress_results = op2_plugin.get_results(param.op2_fp, refine_eids, "stress")
    eid_stress_dict = {}
    for stress_result in stress_results:
        eid_stress_dict[stress_result.eid] = stress_result

    for refine_element in refine_elements:
        center = bdf_plugin.get_center(param.bdf_model, refine_element.eid)
        comp = bdf_plugin.get_component_by_cid(param.bdf_model, refine_element.cid)
        evaluate_fatigue(refine_element, eid_stress_dict.get(refine_element.eid), center, comp.g70_name)
    return Result()
